﻿#include "export_http.hh"
#include "../../../box/service_box.hh"
#include "../../../detail/http_base_impl.hh"
#include "../../../detail/http_data.hh"
#include "../../time_util.hh"
#include "../lua_helper.hh"

#include <functional>

kratos::lua::LuaHttp::LuaHttp(kratos::service::ServiceBox *box, lua_State *L,
                              LuaServiceImpl *service) {
  box_ = box;
  L_ = L;
  service_ = service;
}

kratos::lua::LuaHttp::~LuaHttp() {}

auto kratos::lua::LuaHttp::do_register() -> bool {
  // 设置对象指针到虚拟机
  set_class(L_, this, "_box_http");
  // 在注册表内建立一个表
  lua_reg_key_ = LuaUtil::new_registry_table(L_);
  // 建立HTTP实例
  http_ptr_ = kratos::make_unique_pool_ptr<kratos::http::HttpBaseImpl>(box_);

  LuaUtil::register_function(L_, "kratos_wait_request",
                             &LuaHttp::lua_wait_request);
  LuaUtil::register_function(L_, "kratos_wait_response",
                             &LuaHttp::lua_wait_response);

  box_->write_log_line(klogger::Logger::VERBOSE,
                       "[lua][http]HTTP module installed");

  return true;
}

auto kratos::lua::LuaHttp::update(std::time_t ms) -> void {
  http_ptr_->update(ms);
}

auto kratos::lua::LuaHttp::do_cleanup() -> void {
  if (started_) {
    http_ptr_->stop();
  }
  response_map_.clear();
  LuaUtil::LuaState lua_state(L_);
  lua_state.get_reg_table(lua_reg_key_);
  for (auto index : connector_function_list_) {
    lua_state.remove_table(index, -3);
  }
  for (auto index : listener_function_list_) {
    lua_state.remove_table(index, -3);
  }
  lua_state.pop(1);
  // 销毁注册表key
  LuaUtil::remove_registry_key(L_, lua_reg_key_);
}

auto kratos::lua::LuaHttp::thread_exit_event_handler_server(
    std::uint64_t user_data, LuaThread *thread, bool normal_terminated)
    -> void {
  auto index = (int)user_data; // 未处理完成的response key
  // 当前虚拟机
  LuaUtil::LuaState lua_state(thread);
  // 查找response
  auto it = response_map_.find(index);
  if (it == response_map_.end()) {
    return;
  }
  auto invalid_argument = !lua_state.istable(-1);
  // 非正常终止, 指针无效, 返回值不是表
  if (!normal_terminated || it->second.expired() || invalid_argument) {
    if (invalid_argument) {
      box_->write_log_line(klogger::Logger::FAILURE,
                           "[lua][http]HTTP response is not table");
    }
    response_map_.erase(it);
    return;
  }
  // 调用完成
  auto response = it->second.lock();
  // 取得返回值, lua表->response
  lua_state.copy(*response->get_response().lock());
  // 销毁weak_ptr
  response_map_.erase(it);
  // 返回给客户端
  response->finish();
}

auto kratos::lua::LuaHttp::new_index() -> int { return callback_index_++; }

auto kratos::lua::LuaHttp::get_header(lua_State *l)
    -> const kratos::http::HeaderMap & {
  header_map_.clear();
  LuaUtil::LuaState lua_state(l);
  lua_state.copy(header_map_, -4);
  // 返回
  return header_map_;
}

auto kratos::lua::LuaHttp::add_lua_callback_client(LuaUtil::LuaState &lua_state,
                                                   int index) -> bool {
  if (!lua_state.add_lua_reg_function(lua_reg_key_, index)) {
    return false;
  }
  // 添加到连接器回调链表
  connector_function_list_.push_back(index);
  return true;
}

auto kratos::lua::LuaHttp::remove_lua_callback_client(
    LuaUtil::LuaState &lua_state, int index) -> bool {
  if (!lua_state.remove_lua_reg_function(lua_reg_key_, index)) {
    // 删除连接器回调
    connector_function_list_.remove(index);
    return false;
  }
  // 删除连接器回调
  connector_function_list_.remove(index);
  return true;
}

auto kratos::lua::LuaHttp::add_lua_callback_server(LuaUtil::LuaState &lua_state,
                                                   int index) -> bool {
  if (!lua_state.add_lua_reg_function(lua_reg_key_, index)) {
    return false;
  }
  // 添加到服务器回调链表
  listener_function_list_.push_back(index);
  return true;
}

auto kratos::lua::LuaHttp::http_request_handler(
    kratos::http::HttpCallPtr request, kratos::http::HttpCallPtr response,
    std::uint64_t user_data) -> bool {
  if (request.expired() || response.expired()) {
    box_->write_log_line(
        klogger::Logger::FAILURE,
        "[lua][http]HTTP connection timeout or start HTTP server failed");
    return true;
  }
  // 建立一个协程
  auto thread_ptr = service_->get_thread_manager()->new_lua_thread(0);
  if (!thread_ptr) {
    // 建立协程失败
    box_->write_log_line(klogger::Logger::FAILURE,
                         "[lua][http]HTTP spawn lua thread failed");
    return true;
  }
  LuaUtil::LuaState lua_state(thread_ptr);
  // 获取lua回调并亚入栈顶
  if (!lua_state.get_lua_reg_function(lua_reg_key_, (int)user_data)) {
    box_->write_log_line(klogger::Logger::FAILURE,
                         "[lua][http]HTTP request lua handler not found");
    // 获取表失败
    service_->get_thread_manager()->remove(thread_ptr);
    return true;
  }
  // 建立返回table
  auto response_ptr = response.lock();
  // response压入栈顶
  lua_state.push(request.lock().get());
  // 启动协程调用用户回调，参数为response表
  if (!thread_ptr->resume(1)) {
    // 发生错误
    service_->get_thread_manager()->remove(thread_ptr);
    return true;
  }
  if (thread_ptr->is_yield()) {
    // 处理函数内出让

    // 建立一个新的索引
    auto index = new_index();
    // 添加协程退出事件回调
    thread_ptr->add_exclude_event_handler(
        ThreadEvent::EXIT,
        std::bind(&LuaHttp::thread_exit_event_handler_server, this,
                  std::placeholders::_1, std::placeholders::_2,
                  std::placeholders::_3),
        index);
    // 记录未处理完成的response
    response_map_[index] = response;
    // 加入到出让协成检测
    service_->add_yield_thread(thread_ptr);
    // 处理未完成
    return false;
  } else {
    // 返回值是response表
    if (!lua_state.istable(-1)) {
      box_->write_log_line(klogger::Logger::FAILURE,
                           "[lua][http]HTTP response is not table");
      return true;
    }
    // 获取response, lua表->response
    lua_state.copy(*response_ptr->get_response().lock());
    // 发送response到客户端
    response_ptr->finish();
    // 处理完成
    service_->get_thread_manager()->remove(thread_ptr);
    return true;
  }
}

auto kratos::lua::LuaHttp::thread_exit_event_handler_client(
    std::uint64_t user_data, LuaThread *thread, bool normal_terminated)
    -> void {
  LuaUtil::LuaState lua_state(thread);
  // 销毁回调函数
  remove_lua_callback_client(lua_state, (int)user_data);
}

auto kratos::lua::LuaHttp::http_response_handler(
    kratos::http::HttpCallPtr response, std::uint64_t user_data) -> void {
  auto index = (int)user_data;
  if (response.expired()) {
    box_->write_log_line(klogger::Logger::FAILURE,
                         "[lua][http]Connecto to remote HTTP server failed");
    // 连接失败
    LuaUtil::LuaState error_lua_state(L_);
    remove_lua_callback_client(error_lua_state, index);
    return;
  }
  // 建立一个协程
  auto thread_ptr = service_->get_thread_manager()->new_lua_thread(0);
  if (!thread_ptr) {
    // 建立协程失败
    LuaUtil::LuaState error_lua_state(L_);
    remove_lua_callback_client(error_lua_state, index);
    return;
  }
  // 虚拟机
  LuaUtil::LuaState lua_state(thread_ptr);
  // 获取lua回调函数, 压入栈顶
  if (!lua_state.get_lua_reg_function(lua_reg_key_, index)) {
    box_->write_log_line(klogger::Logger::FAILURE,
                         "[lua][http]HTTP response lua handler not fond");
    // 获取表失败
    remove_lua_callback_client(lua_state, index);
    service_->get_thread_manager()->remove(thread_ptr);
    return;
  }
  // 建立返回table,压入栈顶
  lua_state.push(response.lock().get());
  // 协程内调用lua回调
  if (!thread_ptr->resume(1)) {
    // 调用出错
    remove_lua_callback_client(lua_state, index);
    service_->get_thread_manager()->remove(thread_ptr);
    return;
  }
  if (thread_ptr->is_yield()) {
    // 处理函数内出让

    // 添加协程退出事件回调
    thread_ptr->add_exclude_event_handler(
        ThreadEvent::EXIT,
        std::bind(&LuaHttp::thread_exit_event_handler_client, this,
                  std::placeholders::_1, std::placeholders::_2,
                  std::placeholders::_3),
        index);
    // 添加到出让协程检测
    service_->add_yield_thread(thread_ptr);
  } else {
    // 处理完成
    remove_lua_callback_client(lua_state, index);
    service_->get_thread_manager()->remove(thread_ptr);
  }
  lua_state.pop(1);
}

kratos::lua::LuaHttp *kratos::lua::LuaHttp::get_http(lua_State *l) {
  return LuaExportClass::get_class<LuaHttp>(l, "_box_http");
}

bool kratos::lua::LuaHttp::check_wait_response_param(
    const LuaUtil::LuaState &lua_state) {
  if (!lua_state.isstring(-8) || !lua_state.isinteger(-7) ||
      !lua_state.isstring(-6) || !lua_state.isstring(-5) ||
      !lua_state.istable(-4) || !lua_state.isstring(-3) ||
      !lua_state.isinteger(-2) || !lua_state.isfunction(-1)) {
    return false;
  }
  return true;
}

int kratos::lua::LuaHttp::lua_wait_request(lua_State *l) {
  LuaUtil::LuaState lua_state(l);
  auto *http = get_http(l);
  if (!http) {
    return lua_state.push_nil();
  }
  if (!http->started_) {
    if (!http->http_ptr_->start()) {
      http->box_->write_log_line(klogger::Logger::FAILURE,
                                 "[lua][http]Start HTTP module failed");
      return lua_state.push_nil();
    }
    http->started_ = true;
  }
  if (!lua_state.isstring(-3) || !lua_state.isinteger(-2) ||
      !lua_state.isfunction(-1)) {
    http->box_->write_log_line(
        klogger::Logger::FAILURE,
        "[lua][http]Invalid parameter for wait_request function");
    return lua_state.push_nil();
  }
  auto *host = lua_state.get<const char *>(-3);
  int port = lua_state.get<int>(-2);
  auto index = http->new_index();
  if (!http->http_ptr_->wait_request_async(
          host, port, index,
          std::bind(&LuaHttp::http_request_handler, http, std::placeholders::_1,
                    std::placeholders::_2, std::placeholders::_3))) {
    http->box_->write_log_line(klogger::Logger::FAILURE,
                               "[lua][http]Listen failed[" + std::string(host) +
                                   ":" + std::to_string(port) + "]");
    return lua_state.push_nil();
  }
  if (!http->add_lua_callback_server(lua_state, index)) {
    http->box_->write_log_line(klogger::Logger::FAILURE,
                               "[lua][http]Add lua HTTP server handler failed");
    return lua_state.push_nil();
  }
  return lua_state.push(true);
}

int kratos::lua::LuaHttp::lua_wait_response(lua_State *l) {
  LuaUtil::LuaState lua_state(l);
  auto *http = get_http(l);
  if (!http) {
    return lua_state.push_nil();
  }
  if (!http->started_) {
    // 启动失败
    if (!http->http_ptr_->start()) {
      http->box_->write_log_line(klogger::Logger::FAILURE,
                                 "[lua][http]Start HTTP module failed");
      return lua_state.push_nil();
    }
    // 启动成功
    http->started_ = true;
  }
  if (!check_wait_response_param(lua_state)) {
    http->box_->write_log_line(
        klogger::Logger::FAILURE,
        "[lua][http]Invalid parameter for wait_response function");
    return lua_state.push_nil();
  }
  auto *host = lua_state.get<const char *>(-8);
  auto port = lua_state.get<int>(-7);
  auto *uri = lua_state.get<const char *>(-6);
  auto *method = lua_state.get<const char *>(-5);
  const auto &header_map = http->get_header(l);
  auto *content = lua_state.get<const char *>(-3);
  auto timeout = lua_state.get<int>(-2);
  auto index = http->new_index();
  // 发起一次异步的HTTP request
  if (!http->http_ptr_->do_request_async(
          host, port, uri, method, header_map, content, timeout, index,
          std::bind(&LuaHttp::http_response_handler, http,
                    std::placeholders::_1, std::placeholders::_2))) {
    http->box_->write_log_line(klogger::Logger::FAILURE,
                               "[lua][http]Connect to remote server failed[" +
                                   std::string(host) + ":" +
                                   std::to_string(port) + "]");
    return lua_state.push_nil();
  }
  // 记录lua回调函数
  if (!http->add_lua_callback_client(lua_state, index)) {
    http->box_->write_log_line(klogger::Logger::FAILURE,
                               "[lua][http]Add lua HTTP client handler failed");
    // 建立失败
    return lua_state.push_nil();
  }
  return lua_state.push(true);
}
